package cn.tedu.luban.order.webapi.service.impl;

import cn.tedu.luban.attach.service.IAttachService;
import cn.tedu.luban.commons.enums.order.OrderStatusEnum;
import cn.tedu.luban.commons.exception.LubanServiceException;
import cn.tedu.luban.commons.pojo.attach.dto.AttachDTO;
import cn.tedu.luban.commons.pojo.attach.dto.AttachUpdateDTO;
import cn.tedu.luban.commons.pojo.attach.model.Attach;
import cn.tedu.luban.commons.pojo.order.dto.*;
import cn.tedu.luban.commons.pojo.order.model.Order;
import cn.tedu.luban.commons.pojo.order.model.OrderLog;
import cn.tedu.luban.commons.pojo.order.vo.*;
import cn.tedu.luban.commons.restful.JsonPage;
import cn.tedu.luban.commons.restful.ResponseCode;
import cn.tedu.luban.commons.utils.PropertiesUtils;
import cn.tedu.luban.demand.service.IRequestOrderService;
import cn.tedu.luban.order.service.EventService;
import cn.tedu.luban.order.service.IOrderService;
import cn.tedu.luban.order.webapi.mapper.OrderLogMapper;
import cn.tedu.luban.order.webapi.mapper.OrderMapper;
import cn.tedu.luban.order.webapi.utils.IncUidGenerator;
import cn.tedu.luban.order.webapi.utils.SecurityContext;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderServiceImpl implements IOrderService {
    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderLogMapper orderLogMapper;

    @DubboReference
    private IRequestOrderService dubboRequestOrderService;

    @Autowired
    private EventService eventService;

    @Autowired
    private IncUidGenerator incUidGenerator;

    @DubboReference
    private IAttachService dubboAttachService;

    @Value("${app.business.type}")
    private Integer businessType;

    @Value("${url}")
    private String url;

    //创建订单
    @Override
    @Transactional
    public String create(OrderDTO orderDTO) {
        //dubbo调用需求单模块抢单方法
        Integer num = (Integer) dubboRequestOrderService.grabOrder(
                orderDTO.getRequestOrderNo());
        //判断上面抢单操作对数据影响的行数，如果是1表示成功，0表示失败
        if (num == 0) {
            //如果是0，抛出异常，提示抢单失败
            throw new LubanServiceException(ResponseCode.NOT_ACCEPTABLE, "抢单失败");
        }
        //如果抢单成功，进行生单操作
        //获取用户id
        Long userId = SecurityContext.getUserId();
        //生成订单id---orderNo
        //*订单编号生成
        //*12位时间戳+当前服务器原子性自增长3位+当前服务器ip后三位+当前用户最后一位
        String orderNo = incUidGenerator.generate(userId);
        try {
            //尝试完成生单业务流程
            orderDTO.setUserId(userId);
            doCreate(orderNo, orderDTO);
        } catch (LubanServiceException e) {
            //生单业务出现异常时发消息回滚
            OrderMqDTO orderMqDTO = new OrderMqDTO();
            BeanUtils.copyProperties(orderDTO, orderMqDTO);
            eventService.sendOrderCancelEvent(orderMqDTO);
            log.error("生成订单时发生错误", e);
            throw e;
        }
        return orderNo;
    }

    private void doCreate(String orderNo, OrderDTO orderDTO) {
        //orderDTO赋值
        orderDTO.setOrderNo(orderNo);
        orderDTO.setStatus(OrderStatusEnum.ORDER_CREATE_SUCCESS.getStatus());
        //新增日志
        OrderLog orderLog = new OrderLog();
        orderLog.setOrderNo(orderNo);
        orderLog.setUserId(orderDTO.getUserId());
        orderLog.setOperateName(OrderStatusEnum.ORDER_CREATE_SUCCESS.getMessage());
        orderLog.setRemark(OrderStatusEnum.ORDER_CREATE_SUCCESS.getMessage());
        long now = System.currentTimeMillis();
        orderLog.setOperateTime(now);
        orderLog.setGmtModified(now);
        orderLog.setGmtCreate(now);
        orderLog.setStatus(1);
        orderLog.setCreateUserId(orderDTO.getUserId());
        orderLog.setModifiedUserId(orderDTO.getUserId());
        orderLog.setCreateUserName("mock1");
        orderLog.setModifiedUserName("mock1");
        orderLogMapper.insert(orderLog);
        //新增订单
        Order order = new Order();
        BeanUtils.copyProperties(orderDTO, order);
        order.setGmtCreate(System.currentTimeMillis());
        order.setGmtModified(order.getGmtCreate());
        order.setCreateUserId(orderDTO.getUserId());
        order.setModifiedUserId(orderDTO.getUserId());
        order.setCreateUserName("mock1");
        order.setModifiedUserName("mock1");
        orderMapper.insert(order);
    }

    //查询显示师傅工作台页面的订单列表
    @Override
    public JsonPage<OrderVO> list(
            OrderListDTO orderListDTO,
            Integer pageNo,
            Integer pageSize) {
        //设置分页查询条件
        PageHelper.startPage(pageNo, pageSize);
        //获取用户id
        Long userId = SecurityContext.getUserId();
        orderListDTO.setUserId(userId);
        //执行查询
        List<Order> orderList = orderMapper.queryOrders(orderListDTO);
        //将orderList集合中所有元素的同名属性赋值到orderVOList中
        List<OrderVO> orderVOList = PropertiesUtils.copyListProperties(
                orderList, OrderVO.class);
        //OrderVO和Order有属性不同名,需要单独赋值
        for (int i = 0; i < orderList.size(); i++) {
            orderVOList.get(i).setRequestOrderCategoryName(
                    orderList.get(i).getOrderCategoryName());
        }
        return JsonPage.restPage(new PageInfo<>(orderVOList));
    }

    //订单详情
    @Override
    public OrderDetailVO getOrderByOrderNo(String orderNo) {
        //根据订单号查询订单基本信息
        Order order = orderMapper.getOrderByOrderNo(orderNo);
        //实例化要返回的类型
        OrderDetailVO orderDetailVO = new OrderDetailVO();
        //赋值本订单相关图片
        orderDetailVO.setAttachInfo(getAttachList(order.getId()));
        //定义日志map，这个日志中包含当前订单日志信息例如抢单时间,抵达时间,完成时间等
        Map<String, Long> operateTime = getOperateTime(orderNo);
        //实例化OrderInfoVo
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        BeanUtils.copyProperties(order, orderInfoVo);
        //赋值相关时间点
        orderInfoVo.setSignTime(operateTime.get(
                OrderStatusEnum.ORDER_SIGN_SUCCESS.getMessage()));
        orderInfoVo.setFinishTime(operateTime.get(
                OrderStatusEnum.ORDER_SERVICE_SUCCESS_UN_SETTLE.getMessage()));
        orderInfoVo.setCreateTime(order.getGmtCreate());
        orderDetailVO.setOrderInfo(orderInfoVo);
        //赋值需求单信息,实例化RequestOrderInfoVO
        RequestOrderInfoVO requestOrderInfoVO = new RequestOrderInfoVO();
        BeanUtils.copyProperties(order, requestOrderInfoVO);
        requestOrderInfoVO.setRequestOrderCategoryName(order.getOrderCategoryName());
        orderDetailVO.setRequestOrderInfo(requestOrderInfoVO);
        //赋值用户信息,实例化UserInfoVO
        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtils.copyProperties(order, userInfoVO);
        orderDetailVO.setUserInfo(userInfoVO);
        return orderDetailVO;
    }

    private List<AttachInfoVO> getAttachList(Long id) {
        //实例化AttachDTO以备查询相关图片信息
        AttachDTO attachDTO = new AttachDTO();
        attachDTO.setBusinessId(id.intValue());
        attachDTO.setBusinessType(businessType);
        //dubbo查询图片信息
        List<Attach> attachList = dubboAttachService.getAttachInfo(attachDTO);
        //需要的信息仅仅是图片的url路径，所以需要变换类型
        List<AttachInfoVO> attachInfoVOs = new ArrayList<>();
        if (!attachList.isEmpty()) {
            for (Attach attach : attachList) {
                //从yml配置文件中获取url的信息，和数据查询出的fileUuid属性值拼接为url
                AttachInfoVO attachInfoVO = new AttachInfoVO();
                attachInfoVO.setUrl(url + attach.getFileUuid());
                attachInfoVOs.add(attachInfoVO);
            }
        }
        return attachInfoVOs;
    }

    //查询订单日志相关信息
    private Map<String, Long> getOperateTime(String orderNo) {
        //按orderNo查询相关所有日志
        List<OrderLog> orderLogs = orderLogMapper.getOrderLogByOrderNo(orderNo);
        /*
        日志如果不是null
        使用Java 8的Stream API将orderLogs列表转换为一个映射表，
        其中键为每个订单日志对象的操作名称（通过OrderLog::getOperateName方法引用获取），
        值为每个订单日志对象的操作时间（通过OrderLog::getOperateTime方法引用获取）。
        在转换为映射表时，如果存在相同的操作名称，
        使用(v1, v2) -> v2来决定选择后出现的操作时间覆盖先出现的操作时间。
        返回包含操作名称和操作时间的映射表。
         */
        if (!CollectionUtils.isEmpty(orderLogs)) {
            return orderLogs.stream()
                    .collect(Collectors.toMap(
                            OrderLog::getOperateName,
                            OrderLog::getOperateTime,
                            (v1, v2) -> v2));
        }
        //如果orderLogs列表为空，则返回一个空的映射表Collections.emptyMap()
        return Collections.emptyMap();
    }

    //确认抵达的方法
    @Override
    @Transactional
    public String sign(OrderSignDTO orderSignDTO) {
        //获取用户Id
        Long userId = SecurityContext.getUserId();
        doUpdate(orderSignDTO.getOrderNo(),
                userId, OrderStatusEnum.ORDER_SIGN_SUCCESS);
        return orderSignDTO.getOrderNo();
    }

    private Order doUpdate(
            String orderNo, Long userId, OrderStatusEnum orderStatusEnum) {
        Order order = orderMapper.getOrderByOrderNo(orderNo);
        if (order == null || OrderStatusEnum.isErrorStatus(
                order.getStatus(), orderStatusEnum.getStatus())) {
            throw new LubanServiceException(
                    ResponseCode.INTERNAL_SERVER_ERROR, "订单不存在或订单状态不正确");
        }
        //订单日志的新增
        OrderLog orderLog = new OrderLog();
        orderLog.setOrderNo(orderNo);
        orderLog.setUserId(userId);
        orderLog.setOperateName(orderStatusEnum.getMessage());
        orderLog.setRemark(orderStatusEnum.getMessage());
        long now = System.currentTimeMillis();
        orderLog.setOperateTime(now);
        orderLog.setGmtModified(now);
        orderLog.setGmtCreate(now);
        orderLog.setStatus(1);
        orderLog.setCreateUserId(userId);
        orderLog.setModifiedUserId(userId);
        orderLog.setCreateUserName("mock1");
        orderLog.setModifiedUserName("mock1");
        orderLogMapper.insert(orderLog);
        //订单状态修改为签到
        order.setStatus(orderStatusEnum.getStatus());
        order.setGmtCreate(System.currentTimeMillis());
        order.setGmtModified(order.getGmtCreate());
        order.setCreateUserId(userId);
        order.setModifiedUserId(userId);
        order.setCreateUserName("mock1");
        order.setModifiedUserName("mock1");
        orderMapper.update(order);
        return order;
    }

    //将用户上传的文件信息保存到attach
    @Override
    public String confirm(OrderConfirmDTO orderConfirmDTO) {
        //根据订单编号查询订单对象
        Order order = orderMapper.getOrderByOrderNo(orderConfirmDTO.getOrderNo());
        if (order == null) {
            throw new LubanServiceException(ResponseCode.NOT_FOUND, "找不到订单");
        }
        //调用方法，将参数中所有图片信息保存到attach中
        createAttach(order.getId().intValue(), orderConfirmDTO.getAttachIds());
        return orderConfirmDTO.getOrderNo();
    }

    private void createAttach(int businessId, List<Long> attachIds) {
        List<AttachUpdateDTO> attachUpdateDTOList = new ArrayList<>();
        //attachIds 文件id 前端入参
        attachIds.forEach(id -> {
            AttachUpdateDTO attachUpdateDTO = new AttachUpdateDTO();
            attachUpdateDTO.setId(id.intValue());
            attachUpdateDTO.setBusinessId(businessId);
            attachUpdateDTO.setBusinessType(businessType);
            attachUpdateDTO.setIsCover(0);
            attachUpdateDTOList.add(attachUpdateDTO);
        });
        dubboAttachService.batchUpdateAttachByIdList(attachUpdateDTOList);
    }

    //订单完成
    @Override
    public String finish(OrderFinishDTO orderFinishDTO) {
        //用户信息
        Long userId = SecurityContext.getUserId();
        //修改订单状态为已完成，调用方法修改状态同时新增订单日志
        Order order = doUpdate(orderFinishDTO.getOrderNo(), userId,
                OrderStatusEnum.ORDER_SERVICE_SUCCESS_UN_SETTLE);
        //上面的方法返回了我们修改的订单信息对象，收集订单信息，
        OrderMqDTO orderMqDTO = new OrderMqDTO();
        BeanUtils.copyProperties(order, orderMqDTO);
        orderMqDTO.setWorkerId(order.getUserId());
        orderMqDTO.setRequestOrderCategoryName(order.getOrderCategoryName());
        //将消息发送给RocketMQ
        eventService.sendOrderFinishEvent(orderMqDTO);
        return orderFinishDTO.getOrderNo();
    }

    @Override
    public void finishOrder(String orderNo, Long userId) {
        doUpdate(orderNo, userId, OrderStatusEnum.ORDER_FINISH);
    }
}
